#-*- Coding: UTF-8 -*-
# Using Enum from http://pypi.python.org/pypi/enum/
# check out pyglet
# http://pyglet.googlecode.com/svn/trunk/experimental/pulse/

import sys ,types, string, pprint, exceptions, copy, time #XXX
from signal import *
from ctypes import *
from enum import Enum

PA_SUBSCRIPTION_EVENT_SINK = 0           #/**< Event type: Sink */
PA_SUBSCRIPTION_EVENT_SOURCE = 1         #/**< Event type: Source */
PA_SUBSCRIPTION_EVENT_SINK_INPUT = 2     #/**< Event type: Sink input */
PA_SUBSCRIPTION_EVENT_SOURCE_OUTPUT = 3  #/**< Event type: Source output */
PA_SUBSCRIPTION_EVENT_MODULE = 4         #/**< Event type: Module */
PA_SUBSCRIPTION_EVENT_CLIENT = 5         #/**< Event type: Client */
PA_SUBSCRIPTION_EVENT_SAMPLE_CACHE = 6   #/**< Event type: Sample cache item */
PA_SUBSCRIPTION_EVENT_SERVER = 7         #/**< Event type: Global server change, only occuring with PA_SUBSCRIPTION_EVENT_CHANGE. \since 0.4  */
PA_SUBSCRIPTION_EVENT_AUTOLOAD = 8       #/**< Event type: Autoload table changes. \since 0.5 */
PA_SUBSCRIPTION_EVENT_FACILITY_MASK = 15 #/**< A mask to extract the event type from an event value */

PA_SUBSCRIPTION_EVENT_NEW = 0            #/**< A new object was created */
PA_SUBSCRIPTION_EVENT_CHANGE = 16        #/**< A property of the object was modified */
PA_SUBSCRIPTION_EVENT_REMOVE = 32        #/**< An object was removed */
PA_SUBSCRIPTION_EVENT_TYPE_MASK = 16+32   #/**< A mask to extract the event operation from an event value */

PA_SUBSCRIPTION_MASK_NULL = 0               #/**< No events */
PA_SUBSCRIPTION_MASK_SINK = 1               #/**< Sink events */
PA_SUBSCRIPTION_MASK_SOURCE = 2             #/**< Source events */
PA_SUBSCRIPTION_MASK_SINK_INPUT = 4         #/**< Sink input events */
PA_SUBSCRIPTION_MASK_SOURCE_OUTPUT = 8      #/**< Source output events */
PA_SUBSCRIPTION_MASK_MODULE = 16            #/**< Module events */
PA_SUBSCRIPTION_MASK_CLIENT = 32            #/**< Client events */
PA_SUBSCRIPTION_MASK_SAMPLE_CACHE = 64      #/**< Sample cache events */
PA_SUBSCRIPTION_MASK_SERVER = 128           #/**< Other global server changes. \since 0.4 */
PA_SUBSCRIPTION_MASK_AUTOLOAD = 256         #/**< Autoload table events. \since 0.5 */
PA_SUBSCRIPTION_MASK_ALL = 511               #/**< Catch all events \since 0.8 */
  
#(*pa_context_subscribe_cb_t)(pa_context *c, pa_subscription_event_type_t t, uint32_t idx, void *userdata);
#(*pa_context_success_cb_t) (pa_context *c, int success, void *userdata);
#client_cb(pa_context *, const pa_client_info *i, int eol, void *userdata)

class pa_sample_spec(Structure):
    _slots_ = ["format","rate","channels"]
    _fields_ = [("format",c_int),
                ("rate",c_uint),
                ("channels",c_ubyte)]
                
class pa_channel_map(Structure):
	_fields_ = [("channels", c_ubyte),
				("map", c_int*32)]
				
class pa_cvolume(Structure):
	_fields_ = [("channels", c_ubyte),
				("values", c_int*32)]

class server_info(Structure):
    _fields_ = [
    			("user_name",c_char_p),
                ("host_name",c_char_p),
                ("server_version",c_char_p),
                ("server_name",c_char_p),
                ("sample_spec",pa_sample_spec),
                ("default_sink_name",c_char_p),
                ("default_source_name",c_char_p),
                ("cookie",c_uint)]
                
class client_info(Structure): 
	_fields_ = [
				("index",c_int), 
				("name",c_char_p), 
				("owner_module",c_uint), 
				("driver",c_char_p)]
				
class sink_input_info(Structure):
	_fields_ = [("index",c_int), #4
				("name",c_char_p),#4
				("owner_module",c_uint),#4
				("client",c_uint),#4
				("sink",c_uint),#4
				("sample_spec",pa_sample_spec),#4
				("channel_map",pa_channel_map),#4
				("volume",pa_cvolume),#4
				("buffer_usec",c_longlong),#4
				("sink_usec",c_longlong),#4
				("resample_method",c_char_p),#4
				("driver",c_char_p),#4
				("mute",c_int)]#4
				
class sink_info(Structure):
    _fields_ = [("name",c_char_p),
                ("index",c_int),
                ("desc",c_char_p),
                ("sample_spec",pa_sample_spec),
                ("channel_map",pa_channel_map),
                ("owner_module",c_uint),
                ("volume",pa_cvolume),
                ("mute",c_int),
                ("monitor_source",c_uint),
                ("monitor_source_name",c_char_p),
                ("latency",c_ulong),
                ("driver",c_char_p),
                ("flags",c_int)]


state_cb_func_t = CFUNCTYPE(c_void_p, c_void_p,  (c_void_p))
subscribe_cb_func_t = CFUNCTYPE(c_void_p, c_void_p, c_int, c_int,  (c_void_p))
success_cb_func_t = CFUNCTYPE(c_void_p,c_void_p, c_int,  (c_void_p))
serverinfo_cb_func_t = CFUNCTYPE(c_void_p, c_void_p, POINTER(server_info), (c_void_p))
clientinfo_cb_func_t = CFUNCTYPE(c_void_p, c_void_p, POINTER(client_info), c_int,  (c_void_p))
sinkinfo_cb_func_t = CFUNCTYPE(c_void_p, c_void_p, POINTER(sink_info), c_int,  (c_void_p))
sinkinputinfo_cb_func_t = CFUNCTYPE(c_void_p, c_void_p, POINTER(sink_input_info), c_int,  (c_void_p))

#cdll.LoadLibrary("libpulse.so.0")
libpulse = CDLL("libpulse.so.0")
libglib = CDLL("libglib-2.0.so.0")
libgmloop = CDLL("libpulse-mainloop-glib.so.0")

#Using c_void_p for now or maybe there is no need for else
libglib.g_main_context_default.restype = c_void_p

libgmloop.pa_glib_mainloop_get_api.restype = c_void_p
libgmloop.pa_glib_mainloop_get_api.argtypes = [c_void_p]
libgmloop.pa_glib_mainloop_new.argtypes = [c_void_p]
libgmloop.pa_glib_mainloop_new.restype = c_void_p
libgmloop.pa_glib_mainloop_free.argtypes = [c_void_p]
libgmloop.pa_threaded_mainloop_new.restype = c_void_p

libgmloop.pa_context_unref.argtypes = [c_void_p]
libgmloop.pa_context_new.argtypes = [c_void_p, c_char_p]
libgmloop.pa_context_get_state.argtypes = [c_void_p]
libgmloop.pa_context_get_state.restype = c_int
libgmloop.pa_context_set_state_callback.argtypes = [c_void_p, state_cb_func_t, c_void_p]
libgmloop.pa_context_set_state_callback.restype = c_void_p
libgmloop.pa_context_set_subscribe_callback.argtypes = [c_void_p, subscribe_cb_func_t, c_void_p]
libgmloop.pa_context_set_subscribe_callback.restype = c_void_p
libgmloop.pa_context_connect.argtypes = [c_void_p,c_void_p,c_int,c_void_p]
libgmloop.pa_context_connect.restype = c_int
libgmloop.pa_context_subscribe.argtypes = [c_void_p, c_int, success_cb_func_t, c_void_p]
libgmloop.pa_context_subscribe.restype = c_void_p
libgmloop.pa_strerror.restype = c_char_p
libgmloop.pa_context_new.restype = c_void_p
libgmloop.pa_context_new.argtypes = [c_void_p, c_char_p]
libgmloop.pa_threaded_mainloop_get_api.argtypes = [c_void_p]
libgmloop.pa_threaded_mainloop_get_api.restype = c_void_p
libgmloop.pa_threaded_mainloop_start.argtypes = [c_void_p]
libgmloop.pa_threaded_mainloop_lock.argtypes = [c_void_p]
libgmloop.pa_threaded_mainloop_unlock.argtypes = [c_void_p]
libgmloop.pa_threaded_mainloop_wait.argtypes = [c_void_p]
libgmloop.pa_threaded_mainloop_signal.argtypes = [c_void_p, c_int]
libgmloop.pa_context_get_server_info.argtypes = [c_void_p, serverinfo_cb_func_t, c_void_p ]
libgmloop.pa_context_get_client_info_list.argtypes = [c_void_p, clientinfo_cb_func_t, c_void_p]
libgmloop.pa_context_get_client_info_list.restype = c_void_p
libgmloop.pa_context_get_sink_info_list.argtypes = [c_void_p, sinkinfo_cb_func_t, c_void_p]
libgmloop.pa_context_get_sink_info_list.restype = c_void_p
libgmloop.pa_context_get_sink_input_info_list.argtypes = [c_void_p, sinkinputinfo_cb_func_t, c_void_p]
libgmloop.pa_context_get_sink_input_info_list.restype = c_void_p

libgmloop.pa_context_get_sink_input_info.argtypes = [c_void_p, c_int, sinkinputinfo_cb_func_t, c_void_p]
libgmloop.pa_context_get_sink_input_info.restype = c_void_p
libgmloop.pa_context_get_client_info.argtypes = [c_void_p, c_int, clientinfo_cb_func_t, c_void_p]
libgmloop.pa_context_get_client_info.restype = c_void_p
libgmloop.pa_context_get_sink_info_by_index.argtypes = [c_void_p, c_int, sinkinfo_cb_func_t, c_void_p]
libgmloop.pa_context_get_sink_info_by_index.restype = c_void_p

libpulse.pa_sw_volume_to_linear.argtypes = [pa_cvolume]
libpulse.pa_sw_volume_to_linear.restype = c_double

libgmloop.pa_context_set_sink_input_volume.argtypes = [c_void_p,c_int, POINTER(pa_cvolume), c_void_p, c_void_p]
libgmloop.pa_context_set_sink_input_volume.restype = c_void_p
#(pa_context *c, uint32_t idx, const pa_cvolume *volume, pa_context_success_cb_t cb, void *userdata);

libgmloop.pa_operation_unref.argtypes = [c_void_p]
libgmloop.pa_operation_unref.restype = c_void_p

libpulse.pa_get_library_version.restype = c_char_p
libpulse.pa_bytes_snprint.restype = c_char_p


class Pulse:
    enum_context_state= Enum("PA_CONTEXT_UNCONNECTED",    #/**< The context hasn't been connected yet */
    "PA_CONTEXT_CONNECTING",     #/**< A connection is being established */
    "PA_CONTEXT_AUTHORIZING",    #/**< The client is authorizing itself to the daemon */
    "PA_CONTEXT_SETTING_NAME",   #/**< The client is passing its application name to the daemon */
    "PA_CONTEXT_READY",          #/**< The connection is established, the context is ready to execute operations */
    "PA_CONTEXT_FAILED",         #/**< The connection failed or was disconnected */
    "PA_CONTEXT_TERMINATED")      #/**< The connection was terminated cleanly */
    
    enum_error = Enum("PA_OK",       #/**< No error */
    "PA_ERR_ACCESS",                 #/**< Access failure */
    "PA_ERR_COMMAND",                #/**< Unknown command */
    "PA_ERR_INVALID",                #/**< Invalid argument */
    "PA_ERR_EXIST",                  #/**< Entity exists */
    "PA_ERR_NOENTITY",               #/**< No such entity */
    "PA_ERR_CONNECTIONREFUSED",      #/**< Connection refused */
    "PA_ERR_PROTOCOL",               #/**< Protocol error */
    "PA_ERR_TIMEOUT",                #/**< Timeout */
    "PA_ERR_AUTHKEY",                #/**< No authorization key */
    "PA_ERR_INTERNAL",               #/**< Internal error */
    "PA_ERR_CONNECTIONTERMINATED",   #/**< Connection terminated */
    "PA_ERR_KILLED",                 #/**< Entity killed */
    "PA_ERR_INVALIDSERVER",          #/**< Invalid server */
    "PA_ERR_MODINITFAILED",          #/**< Module initialization failed */
    "PA_ERR_BADSTATE",               #/**< Bad state */
    "PA_ERR_NODATA",                 #/**< No data */
    "PA_ERR_VERSION",                #/**< Incompatible protocol version \since 0.8 */
    "PA_ERR_TOOLARGE",               #/**< Data too large \since 0.8.1 */
    "PA_ERR_NOTSUPPORTED",           #/**< Operation not supported \since 0.9.5 */
    "PA_ERR_MAX")                    #/**< Not really an error but the first invalid error code */
    
    #enum_subs_mask = Enum("PA_SUBSCRIPTION_MASK_NULL",               #/**< No events */
    #"PA_SUBSCRIPTION_MASK_SINK", #= 1,               #/**< Sink events */
    #"PA_SUBSCRIPTION_MASK_SOURCE", #= 2,             #/**< Source events */
    #"PA_SUBSCRIPTION_MASK_SINK_INPUT", #= 4,         #/**< Sink input events */
    #"PA_SUBSCRIPTION_MASK_SOURCE_OUTPUT", #= 8,      #/**< Source output events */
    #"PA_SUBSCRIPTION_MASK_MODULE", #= 16,            #/**< Module events */
    #"PA_SUBSCRIPTION_MASK_CLIENT", #= 32,            #/**< Client events */
    #"PA_SUBSCRIPTION_MASK_SAMPLE_CACHE", #= 64,      #/**< Sample cache events */
    #"PA_SUBSCRIPTION_MASK_SERVER", #= 128,           #/**< Other global server changes. \since 0.4 */
    #"PA_SUBSCRIPTION_MASK_AUTOLOAD") #= 256,         #/**< Autoload table events. \since 0.5 */
    #PA_SUBSCRIPTION_MASK_ALL = 511               #/**< Catch all events \since 0.8 */
    
    enum_op = Enum("PA_OPERATION_RUNNING",      #/**< The operation is still running */
    "PA_OPERATION_DONE",         #/**< The operation has been completed */
    "PA_OPERATION_CANCELED")      #/**< The operation has been canceled */
    
    def __init__(self,name,event_cb):
        self.__name__ = name
        self.closed = False
        self.user_event_cb = event_cb
        self.mainloop = None
        self.api = None
        self.context = None
        self.server_info = None
        self.sinks = {}
        self.clients = {}
        
        
        #Persist callback types
        self.__pa_subscribe_cb = subscribe_cb_func_t(self.py_subscribe_cb)
        self.__pa_state_cb = state_cb_func_t(self.py_state_cb)
        self.__pa_success_cb = success_cb_func_t(self.py_success_cb)
        self.__pa_serverinfo_cb = serverinfo_cb_func_t(self.py_serverinfo_cb)
        self.__pa_clientinfo_cb = clientinfo_cb_func_t(self.py_clientinfo_cb)
        self.__pa_sinkinfo_cb = sinkinfo_cb_func_t(self.py_sinkinfo_cb)
        self.__pa_sinkinfoinput_cb = sinkinputinfo_cb_func_t(self.py_sinkinputinfo_cb)
        
        #Set up PulseAudio
        self.pulse_open(name)

    
    def __del__(self):
        if not self.closed:
            self.pulse_close()


    def formatbytes(self,size):
        return libpulse.pa_bytes_snprint(create_string_buffer(256),256,size)
        
    def py_serverinfo_cb(pulse,context,svinfo,user=None):
        #pulse.thread_signal(0)
        
        if svinfo:
            i = svinfo.contents
            pulse.server_info = server_info(i.user_name,i.host_name,i.server_version,
                                i.server_name,i.sample_spec,i.default_sink_name,
        	                    i.default_source_name,i.cookie)

    def py_clientinfo_cb(pulse,context, cinfo, eol, user=None):
        #pulse.thread_signal(0)
        if cinfo:
            i = cinfo.contents
            #print "\tClient:", i.name, eol
            if pulse.clients.has_key(i.index):
                del pulse.clients[i.index]
                pulse.clients[cinfo.contents.index] = client_info(i.index,i.name,i.owner_module,i.driver)
                pulse.user_event_cb(PA_SUBSCRIPTION_EVENT_CLIENT ,
                                PA_SUBSCRIPTION_EVENT_CHANGE , i.index)
            else:
                pulse.clients[i.index] = client_info(i.index,i.name,i.owner_module,i.driver)
                pulse.user_event_cb(PA_SUBSCRIPTION_EVENT_CLIENT ,
                                PA_SUBSCRIPTION_EVENT_NEW , i.index)

    def py_sinkinfo_cb(pulse,context, sinfo, eol, user=None):
        #pulse.thread_signal(0)
        if sinfo:
            print "\tSink:",sinfo.contents.name #,sinfo.contents.index
    
    def py_sinkinputinfo_cb(pulse,context, siinfo, eol, user=None):
        #pulse.thread_signal(0)
        if siinfo:
            i = siinfo.contents
            #print "\tSink input:",i.name
            #if not (pulse and pulse.sinks):
             #   return
             
            si = sink_input_info(i.index,i.name,i.owner_module,i.client,
                            i.sink,i.sample_spec,i.channel_map,i.volume,i.buffer_usec,
                            i.sink_usec,i.resample_method,i.driver,i.mute)
            
            if pulse.sinks.has_key(siinfo.contents.index):
                del pulse.sinks[siinfo.contents.index]
                pulse.sinks[siinfo.contents.index] = si
                pulse.user_event_cb(PA_SUBSCRIPTION_EVENT_SINK_INPUT ,
                                PA_SUBSCRIPTION_EVENT_CHANGE, i.index)
            else:
                pulse.sinks[siinfo.contents.index] = si
                pulse.user_event_cb(PA_SUBSCRIPTION_EVENT_SINK_INPUT ,
                                PA_SUBSCRIPTION_EVENT_NEW, i.index)
            
    def py_subscribe_cb(pulse,context,event,idx ,userdata = None):
        #pulse.thread_signal(0)
        
        e = event & PA_SUBSCRIPTION_EVENT_FACILITY_MASK
        t = (event & PA_SUBSCRIPTION_EVENT_TYPE_MASK)
        
        #print "Subscribe:", e, t
        
        if e == PA_SUBSCRIPTION_EVENT_CLIENT:
            if t == PA_SUBSCRIPTION_EVENT_REMOVE:
                if pulse.user_event_cb:
                    pulse.user_event_cb(e,t,idx)
                del pulse.clients[idx]
                print "Client removed"
            if t == PA_SUBSCRIPTION_EVENT_NEW or t == PA_SUBSCRIPTION_EVENT_CHANGE:
                op = pulse.op_get_ci_index(idx)
                if op:
                    pulse.operation_unref(op)
                print "New client or changed"
        
        if e == PA_SUBSCRIPTION_EVENT_SINK_INPUT:
            if t == PA_SUBSCRIPTION_EVENT_REMOVE:
                if pulse.user_event_cb:
                    pulse.user_event_cb(e,t,idx)
                del pulse.sinks[idx]
                print "Sink removed"
            if t == PA_SUBSCRIPTION_EVENT_NEW or t == PA_SUBSCRIPTION_EVENT_CHANGE:
                op = pulse.op_get_sinput_index(idx)
                if op:
                    pulse.operation_unref(op)
                print "New sink or changed"
        
        if e == PA_SUBSCRIPTION_EVENT_SERVER:
            pass
                
        #if pulse.user_event_cb:
        #    pulse.user_event_cb(event,idx)
        
    
    def py_success_cb(pulse,context,success,user=None):
        #pulse.thread_signal(0)
    	print "Success:", (success == 1)
    
    def py_state_cb(pulse,context,userdata = None):
        state = pulse.get_context_state()
        #print "py_state_cb:", state, userdata
        #if state == pulse.enum_context_state.PA_CONTEXT_READY or state == pulse.enum_context_state.PA_CONTEXT_TERMINATED or state == pulse.enum_context_state.PA_CONTEXT_FAILED: 
            #pulse.thread_signal(0)
            
        if state == pulse.enum_context_state.PA_CONTEXT_READY:
            mask = (PA_SUBSCRIPTION_MASK_SINK | PA_SUBSCRIPTION_MASK_SOURCE | 
                    PA_SUBSCRIPTION_MASK_SINK_INPUT | PA_SUBSCRIPTION_MASK_CLIENT | 
                    PA_SUBSCRIPTION_MASK_SERVER)

            pulse.set_subscribe_callback(pulse.py_subscribe_cb,None)
            
            op = pulse.subscribe(mask,None)
            #while self.get_op_state(op) != self.enum_op.PA_OPERATION_DONE:
            #    self.thread_wait()
            
            if not op:
                print "Subscribing failed:", op, pulse.get_error()
                return #self.unlock_and_fail()
            else:
                print "Subscribed"
                pulse.operation_unref(op)
            
            op = pulse.op_get_server_info()
            #while self.get_op_state(op) != self.enum_op.PA_OPERATION_DONE:
            #    self.thread_wait()
            if not op:
                print "Sink Op failed:", op, pulse.get_error()
                #return self.unlock_and_fail()
            else:
                #print "Sink Op success:",pulse.get_op_state(op)
                pulse.operation_unref(op)    
                        
            op = pulse.op_get_client_info()
            #while self.get_op_state(op) != self.enum_op.PA_OPERATION_DONE:
            #    self.thread_wait()
            
            if not op:
                print "Getting client info failed:", op, pulse.get_error()
                return #self.unlock_and_fail()
            else:
                #print "Got client info"
                pulse.operation_unref(op)
                                
            op = pulse.op_get_sink_info()
            #while self.get_op_state(op) != self.enum_op.PA_OPERATION_DONE:
            #    self.thread_wait()
            if not op:
                print "Op failed:", op, pulse.get_error()
                #return self.unlock_and_fail()
            else:
                #print "Op success:",pulse.get_op_state(op)
                pulse.operation_unref(op)
                
            op = pulse.op_get_sink_input_info()
            #while self.get_op_state(op) != self.enum_op.PA_OPERATION_DONE:
            #    self.thread_wait()
            if not op:
                #print "Sink Op failed:", op, self.get_error()
                #return self.unlock_and_fail()
                pass
            else:
                #print "Sink Op success:",self.get_op_state(op)
                pulse.operation_unref(op)

        return
        
    def get_context_state(self):
        return self.enum_context_state[ libgmloop.pa_context_get_state(self.context) ]

    def version(self):
        return libpulse.pa_get_library_version()
        
    def get_context(self):
        return libglib.g_main_context_default()
        
    def pulse_open(self,name):
        
        self.mainloop = libgmloop.pa_glib_mainloop_new(self.get_context()) #pa_threaded_mainloop_new()
        if not self.mainloop:
            print "Loop:",self.get_error()
            return self.unlock_and_fail()

        #self.thread_lock()
        
        self.api = libgmloop.pa_glib_mainloop_get_api(self.mainloop)
        if not self.api:
            print "Api:",self.get_error()
            return self.unlock_and_fail()
            
        self.context = libgmloop.pa_context_new(self.api,name)
        if not self.context:
            print "Context:",self.get_error()
            return self.unlock_and_fail()
            
        self.set_state_callback(self.py_state_cb,None)
        
        if self.connect_context() < 0:
            print "Connect:",self.get_error()
            return self.unlock_and_fail()
        
        #if libgmloop.pa_threaded_mainloop_start(self.mainloop) <0:
        #    print "Loop start:",self.get_error()
        #    return self.unlock_and_fail()
            #libgmloop.pa_threaded_mainloop_wait(self.mainloop)
            
        #print "Wait til context ready"
        #self.thread_wait()
           
        #self.thread_unlock()
        return True
        
    def pulse_close(self):
        
        if self.closed:
            return
        print "Closing down"
        #if self.mainloop:
        #    libgmloop.pa_threaded_mainloop_stop(self.mainloop)

        print "Disconnecting context"
        if self.context:
            libgmloop.pa_context_disconnect(self.context)
            print "unreffing"
            libgmloop.pa_context_unref(self.context)
            self.context = None

        print "Freeing main loop"
        if self.mainloop:
            libgmloop.pa_glib_mainloop_free(self.mainloop)
            self.mainloop = None
        self.closed = True

    def unlock_and_fail(self):
        print "unlock and fail"
        #self.thread_unlock()
        self.pulse_close()
        return False
    
    def thread_unlock(self):
        libgmloop.pa_threaded_mainloop_unlock(self.mainloop)
        
    def thread_lock(self):
        libgmloop.pa_threaded_mainloop_lock(self.mainloop)
    
    def thread_wait(self):
        libgmloop.pa_threaded_mainloop_wait(self.mainloop)
    
    def thread_signal(self,sig):
        libgmloop.pa_threaded_mainloop_signal(self.mainloop, sig);
    
    def get_op_state(self,op):
        return self.enum_op[ libgmloop.pa_operation_get_state(op) ]
    
    def op_get_server_info(self):
        return libgmloop.pa_context_get_server_info(self.context,self.__pa_serverinfo_cb,None)
    
    def op_get_client_info(self):
        return libgmloop.pa_context_get_client_info_list(self.context,self.__pa_clientinfo_cb,None)
    
    def op_get_ci_index(self,idx):
        return libgmloop.pa_context_get_client_info(self.context,idx,self.__pa_clientinfo_cb,None)

    def op_get_sink_info(self):
        return libgmloop.pa_context_get_sink_info_list(self.context,self.__pa_sinkinfo_cb,None)
    
    def op_get_si_index(self,idx):
        return libgmloop.pa_context_get_sink_info_by_index(self.context,idx,self.__pa_sinkinfo_cb,None)
    
    def op_get_sink_input_info(self):
        return libgmloop.pa_context_get_sink_input_info_list(self.context,self.__pa_sinkinfoinput_cb,None)

    def op_get_sinput_index(self,idx):
        return libgmloop.pa_context_get_sink_input_info(self.context,idx,self.__pa_sinkinfoinput_cb,None)

    def op_set_sink_input_volume(self,idx,vol):
        op = libgmloop.pa_context_set_sink_input_volume(self.context,idx, vol, self.__pa_success_cb,None) 
        self.operation_unref(op)
                    
    def set_state_callback(self,cb,userdata=None):
        libgmloop.pa_context_set_state_callback(self.context,self.__pa_state_cb,userdata)
    
    def set_subscribe_callback(self,cb,userdata=None):
        return libgmloop.pa_context_set_subscribe_callback(self.context,self.__pa_subscribe_cb,None)
        
    def connect_context(self):
        return libgmloop.pa_context_connect(self.context,None,0,None)
    
    def subscribe(self,mask,user = None):
        return libgmloop.pa_context_subscribe(self.context,mask,self.__pa_success_cb,user)
    
    def operation_unref(self,op):
        libgmloop.pa_operation_unref(op)
        
    def get_linear_volume(self,cvol):
        return libpulse.pa_sw_volume_to_linear(cvol)
        
    def get_error(self):
        return libgmloop.pa_strerror(libgmloop.pa_context_errno(self.context))
    
## 	signal(SIGPIPE,SIG_IGN)
